/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 * Created on 15.08.2007
 * @author Asia Grzibovska
 *
 */

package uk.ac.sanger.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import uk.ac.sanger.*;

/**
 * Servlet implementation class for Servlet: AsjaTestServlet
 * 
 */

public class FeatureUpload extends HttpServlet implements Servlet {

// database servlet
	 public static final String WRITEBACK_SERVER = "http://localhost:8088/writebackServlet/writebackDatabase";

// dazzle servlet
//	public static final String WRITEBACK_SERVER = "http://localhost:8088/ExampleDynamicWeb/post_writeback";

	public static final String CREATE_CMD = "1";
	public static final String MODIFY_CMD = "2";
	public static final String DELETE_CMD = "3";
	public static final String UNDEFINED_CMD = "NA";

	public static final String RESULT_OK = "OK";

	// Don't include check boxes.
	public final int NR_OF_PARAMS = 13;

	FeatureBean f = new FeatureBean();

	// collection of missing fields
	String missing = "";
	// These are the names of variables.
	String[] parms = { "featureid", "featuretype", "method", "segmentid",
			"start", "end", "strand", "phase", "score", "attributes", "link",
			// "system",
			"commit_msg", "groups" };

	// 1-add,2-modify,3-delete
	String mode = "";

	boolean newFeature = true;

	public void init(ServletConfig config) throws ServletException {
		super.init(config);
	}

	// This is the all-important doPost() method.
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			// error=true if verification failed
			boolean error = false;

			// exchange data with JSP through session attributes
			HttpSession session = request.getSession(true);
	
			// first check open id
			if ((String) session.getAttribute("openid") == null) {
				String message = "you are not logged in, please log in first!";
				session.setAttribute("message", message);
				response.sendRedirect("login.jsp");
				error = true;
				return;
			} else
				f.setUser((String) session.getAttribute("openid"));

			if (session != null) {

				// get coordinate system_uri
				String system_uri = (String) session.getAttribute("system_uri");
				session.setAttribute("system_uri", system_uri);
				f.setSystem_uri(system_uri);

				// coordinate system id from uri, system name
				DataManager dbM = new DataManager();
				String system = "" + dbM.getIdFromUri(system_uri);
				String system_name = dbM.new_system_name;
				session.setAttribute("system_name", system_name);
				session.setAttribute("system", system);

					if (newFeature) {
					for (int n = 0; n < NR_OF_PARAMS - 1; n++) {
						session.setAttribute(parms[n], request
								.getParameter(parms[n]));
					}
					// get submit button text : add,modify,delete
					if (request.getParameter("submit").trim().equals("ADD"))
						mode = CREATE_CMD;
					if (request.getParameter("submit").trim().equals("MODIFY"))
						mode = MODIFY_CMD;
					if (request.getParameter("submit").trim().equals("DELETE"))
						mode = DELETE_CMD;
					// set up session attribute
					session.setAttribute("mode", mode);
				}

				// no check values for delete
				if (mode.equals(DELETE_CMD)) {
					f.setCommitMsg(request.getParameter("commit_msg"));
					f.setUser((String) session.getAttribute("openid"));
				} else if (newFeature) {
					// check submitted values
					error = checkValues(request);
				}

				// If any required field has been left blank,
				// we display an error page.
				if (error == true) { // no check values for delete
					if (!mode.equals(DELETE_CMD)) {
						// send redirect to first page with error message
						// request.getRequestDispatcher("writeback.jsp?arg=1&missing="+missing).forward(request,
						// response);
						response.sendRedirect("writeback.jsp?arg=1&missing="
								+ missing + "&uri=" + system_uri);
					}
				}
				// If no important items have been left blank,
				// update the database with the user's input.
				else {
					String reply = "";

					if (newFeature) {
						// BEFORE:write features directly
						// writeFeaturesHTTP();

						// NOW:
						// get connection
						URLConnection connection = getConnection(WRITEBACK_SERVER);
						// construct XML file, send to WritebackDatabase
						if (connection != null) {

							// get output stream
							OutputStreamWriter out = getOutputStream(connection);
							// BufferedReader in = getInputStream(connection);
							if (out != null) {
								// construct XML file, send to WritebackDatabase
								reply = writeFeaturesHTTP(out, connection, f,
										mode);
							}

						}

					}

					if (mode.equals(DELETE_CMD)) {
						if (!reply.equalsIgnoreCase(RESULT_OK))
							response
									.sendRedirect("writeback.jsp?arg=1&missing="
											+ reply + "&uri=" + system_uri);
						else
							// delete mode, redirect to show_features page
							response.sendRedirect("show_features.jsp?action=show&group=&uri="
									+ system_uri);
					} 
					else if (mode.equals(MODIFY_CMD)) {
						if (!reply.equalsIgnoreCase(RESULT_OK))
							response
									.sendRedirect("writeback.jsp?arg=1&missing="
											+ reply + "&uri=" + system_uri);
						else
							// modify mode, redirect to show_features page
							response.sendRedirect("show_features.jsp?action=show&group=&uri="
									+ system_uri);
					} 
					else if (newFeature) {
						if (!reply.equalsIgnoreCase(RESULT_OK))
							response
									.sendRedirect("writeback.jsp?arg=1&missing="
											+ reply + "&uri=" + system_uri);
						else
							// add new mode, send redirect to feature upload
							// page
							response
									.sendRedirect("writeback.jsp?arg=2&missing=none&uri="
											+ system_uri);
					} 
					else {
						
						// if (!reply.equalsIgnoreCase("OK")){}
						// response.sendRedirect("writeback.jsp?arg=1&missing="+reply
						// + "&uri="+system_uri);
						// else
						// send redirect to feature upload page without
						// arguments
		
						response.sendRedirect("writeback.jsp?arg=2&uri="
								+ system_uri + "&id=" + f.getFeatureid());
					}

					// remove attributes after use
					Enumeration attrs = request.getAttributeNames();
					while (attrs.hasMoreElements()) {
						request.removeAttribute(attrs.toString());
						attrs.nextElement();
					}
				}
				// reset variable
				newFeature = true;
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServletException(e.getMessage());
		}
	}

	// doGet method, used when passing data from show_features.jsp
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		try {
			// will be included into URL in POST method
			f.setFeatureid(request.getParameter("featureid"));

			HttpSession session = request.getSession(true);
			session.setAttribute("system_uri", request.getParameter("uri"));

			// undefined mode, means "no mode checks"
			mode = UNDEFINED_CMD;
			// not new feature
			newFeature = false;
			doPost(request, response);
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServletException(e.getMessage());
		}
	}

	// returns true if input string is in INTEGER format
	public boolean isParsableToInt(String i) {
		try {
			Integer.parseInt(i);
			return true;
		} catch (NumberFormatException nfe) {
			return false;
		}
	}

	// returns true if input string is in FLOAT format
	public boolean isParsableToFloat(String i) {
		try {
			Float.parseFloat(i);
			return true;
		} catch (NumberFormatException nfe) {
			return false;
		}
	}

	// the method checks inserted by user values and packs them into the list
	public boolean checkValues(HttpServletRequest request) {
		try { // initialize return variable
			boolean error = false;
			// These are the same variables, expressed as
			// labels that are written in English and look nicer
			// on the page.
			String[] labels = { "Feature id", // 0
					"Feature type", // 1
					"Method", // 2
					"Segment id", // 3
					"Start", // 4
					"End", // 5
					"Strand", // 6
					"Phase", // 7
					"Score", // 8
					"Attributes", // 9
					"Link", // 10
					"commit_message" // 11
			};

			String[] entries = { "", "", "", "", "", "", "", "", "", "", "",
					"", "", "", "", "", "", "", "" };
			String s = "";
			// Check some fields.
			for (int n = 0; n < NR_OF_PARAMS; n++) {

				// get parameter
				s = request.getParameter(parms[n]);
			
				// no value found
				if (s == null)
					System.out.println("did not get a value for " + parms[n]);
				// set start=end=0 if no position
				if ((n == 4 || n == 5) & request.getParameter("no_pos") != null)
			    	s = "0";
				//REMOVED check start-end because
				//coordinates can be alphanumerical 
				//in PDB files
				// check start,end (integer)
				/*if ((n == 4 || n == 5) & s.length() > 0) {
					if (!isParsableToInt(s)) {
						entries[n] = "ERROR";
						labels[n] = labels[n] + " (must be numerical!)";
						error = true;
					}
				}
				// check score (float)
				if (n == 8 & s.length() > 0 & !s.equalsIgnoreCase("null")) {
					if (!isParsableToFloat(s) & s.length() > 0) {
						entries[8] = "ERROR";
						labels[8] = labels[8] + " (must be numerical!)";
						error = true;
					}
				}*/
				// Check carefully to see if any items are blank.
				if (s.length() == 0) {
					// Some fields aren't required, thus
					// can be left blank without generating errors.
					switch (n) {
					// These are the important ones.
					case 0: // FEATURE ID
					case 1: // FEATURE TYPE
					case 2: // METHOD
					case 3: // SEGMENT ID
					case 4: // START
					case 5: // END
					case 6: // STRAND
					case 7: // PHASE
						entries[n] = "ERROR";
						error = true;
						break;
					}
				}

				// all fields OK
				// save values in a list, for parsing to XML
				if (parms[n].equals("featureid"))
					f.setFeatureid(s);
				if (parms[n].equals("featuretype"))
					f.setFeaturetype(s);
				if (parms[n].equals("method"))
					f.setMethod(s);
				if (parms[n].equals("segmentid"))
					f.setSegment(s);
				if (parms[n].equals("start"))
					f.setStart(s);
				if (parms[n].equals("end"))
					f.setEnd(s);
				if (parms[n].equals("strand"))
					f.setStrand(s);
				if (parms[n].equals("phase"))
					f.setPhase(s);
				if (parms[n].equals("score"))
					f.setScore(s);
				if (parms[n].equals("attributes"))
					f.setNotes(s);
				if (parms[n].equals("link"))
					f.setLinks(s);
				if (parms[n].equals("commit_msg"))
					f.setCommitMsg(s);
				if (parms[n].equals("groups"))
					f.setGroups(s);
			}

			// If any required field has been left blank,
			// we display an error page.
			missing = "";
			if (error == true) {
				for (int n = 0; n < NR_OF_PARAMS; n++) {
					if (entries[n] == "ERROR") {
						missing = missing + labels[n] + "@";
						entries[n] = "";
					}
				}
			}
			return error;
		} catch (Exception e) {
			e.printStackTrace();
			// return error=true
			return true;
		}
	}

	public String run_writeback(FeatureBean f, String mode) {
		String reply = "";
		try {
			// get connection
			URLConnection connection = getConnection(WRITEBACK_SERVER);

			// construct XML file, send to Writeback
			if (connection != null) {

				// get output stream
				OutputStreamWriter out = getOutputStream(connection);
				reply = writeFeaturesHTTP(out, connection, f, mode);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return reply;
	}

	public URLConnection getConnection(String Writeback_Server) {
		try {
			URL url = new URL(Writeback_Server);
			URLConnection connection = url.openConnection();

			return connection;
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	// get OutputStream
	public OutputStreamWriter getOutputStream(URLConnection connection) {
		try {

			// get connections from getConnection method
			connection.setDoOutput(true);

			OutputStreamWriter out = new OutputStreamWriter(connection
					.getOutputStream());

			return out;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// get InputStream
	public BufferedReader getInputStream(URLConnection connection) {
		try {

			BufferedReader in = new BufferedReader(new InputStreamReader(
					connection.getInputStream()));

			return in;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// construct XML file, send to HTTP next servlet
	public String writeFeaturesHTTP(OutputStreamWriter out,
			URLConnection connection, FeatureBean f, String mode)
			throws IOException {
		try {
			/*
			 * URL url = new URL(WRITEBACK_SERVER); URLConnection connection =
			 * url.openConnection(); connection.setDoOutput(true);
			 * 
			 * OutputStreamWriter out = new OutputStreamWriter(
			 * connection.getOutputStream());
			 */

			PrintWriter pw = new PrintWriter(out);
			MyPrettyXMLWriter xw = new MyPrettyXMLWriter(pw);
			
			//Content-Type: application/x-das-writeback+xml
            xw.openTag("WRITEBACK");
			xw.attribute("xmlns", "http://biodas.org/documents/das2");
			xw.attribute("xml:base", "http://example.com/das2/dasypus/April2006/");
			xw.openTag("MESSAGE");
			xw.print(f.getCommitMsg()); // commit message
			xw.closeTag("MESSAGE");
			if (mode.equals(MODIFY_CMD)) {
				xw.openTag("UPDATES");
				xw.openTag("UPDATE");
				xw.attribute("uri", f.getFeatureid());
				xw.closeTag("UPDATE");
				xw.closeTag("UPDATES");
			}
			if (mode.equals(DELETE_CMD)) {
				xw.openTag("DELETES");
				xw.openTag("DELETE");
				xw.attribute("uri", f.getFeatureid());
				xw.closeTag("DELETE");
				xw.closeTag("DELETES");
				xw.openTag("FEATURE ");
			} else {
				xw.openTag("FEATURE ");
				xw.attribute("featureid", f.getFeatureid());
				xw.attribute("featuretype", f.getFeaturetype());
				xw.attribute("method", f.getMethod());
				xw.openTag("LOC");
				xw.attribute("segment", f.getSegment());
				xw.attribute("range", f.getStart() + ":" + f.getEnd() + ":"
						+ f.getStrand());
				xw.closeTag("LOC");
				xw.openTag("PROPERTY");
				xw.attribute("phase", f.getPhase());
				xw.closeTag("PROPERTY");
				xw.openTag("PROPERTY");
				xw.attribute("score", f.getScore());
				xw.closeTag("PROPERTY");
				xw.openTag("PROPERTY");
				xw.attribute("link", f.getLinks());
				xw.closeTag("PROPERTY");
				xw.openTag("PROPERTY");
				xw.attribute("groups", f.getGroups());
				xw.closeTag("PROPERTY");
			}
			xw.openTag("PROPERTY");
			xw.attribute("note", f.getNotes());
			xw.closeTag("PROPERTY");
			xw.openTag("PROPERTY");
			xw.attribute("user", f.getUser());
			xw.closeTag("PROPERTY");
			xw.openTag("PROPERTY");
			xw.attribute("system_uri", f.getSystem_uri());
			xw.closeTag("PROPERTY");

			xw.closeTag("FEATURE");

			xw.closeTag("WRITEBACK");
			xw.close();
			out.close();



			BufferedReader in = getInputStream(connection);
			/*
			 * BufferedReader in = new BufferedReader( new InputStreamReader(
			 * connection.getInputStream()));
			 */
			
			String line = null;
			String result = "";
			while ((line = in.readLine()) != null) {
				result = line;
				// for test - print the result
				 System.out.println("submit got response " + line);
			}
			in.close();
			return result;

		} catch (Exception e) {
			e.printStackTrace();
			return "error";
		}
	}
}
